package binding

import (
	"errors"
	"fmt"
	"reflect"
)

/**
  @author: light
  @since: 2023/6/2
  @desc: 参数校验器
*/

type ValidationRule struct {
	Name      string
	Validator func(interface{}) error
}

type validator struct {
	rules map[string][]ValidationRule
	args  []reflect.Value
}

func NewValidator(args []reflect.Value, rule ...ValidationRule) *validator {
	validate := &validator{args: args}
	if 0 == len(rule) {
		rule = append(rule, new(defaultRule).ValidationRule())
	}
	validate.addRule(rule...)
	return validate
}

type CustomRule interface {
	ValidationRule() ValidationRule
}

type defaultRule struct{}

func (d *defaultRule) ValidationRule() ValidationRule {
	requiredRule := ValidationRule{Name: "required",
		Validator: func(data interface{}) error {
			if nil == data || reflect.ValueOf(data).IsZero() {
				return errors.New("is required")
			}
			return nil
		}}
	return requiredRule
}

func (v *validator) addRule(rule ...ValidationRule) {
	if v.rules == nil {
		v.rules = make(map[string][]ValidationRule)
	}
	for _, arg := range v.args {
		value := arg.Elem()
		fields := value.NumField()
		for i := 0; i < fields; i++ {
			for _, r := range rule {
				if r.Name == value.Type().Field(i).Tag.Get("validator") {
					name := value.Type().Field(i).Name
					v.rules[name] = append(v.rules[name], rule...)
				}
			}
		}
	}
}

func (v *validator) Validate() error {
	for _, arg := range v.args {
		for fieldName, rules := range v.rules {
			field := arg.Elem().FieldByName(fieldName)
			for _, rule := range rules {
				err := rule.Validator(field.Interface())
				if err != nil {
					return fmt.Errorf("[%s] %s", fieldName, err.Error())
				}
			}
		}
	}
	return nil
}
